using System;
using System.Web;
using System.Xml;
using Common.Logging;
using Triton.Controller.Command;
using Triton.Controller.Request;
using Triton.Utilities.Configuration;

namespace Triton.Controller
{

	#region History

	// History:
	// 06/02/2009	KP  Changed the logging to Common.logging.
	// 08/12/2009	GV	Rename of BizAction back to Action
	// 09/29/2009	KP	Renamed logging methods to use GetCurrentClassLogger method

	#endregion

	/// <summary>
	/// <b>XmlController</b> gets incoming XML requests from IIS and delegates processing
	/// to the appropriate Command.
	/// </summary>
	///	<author>Scott Dyke</author>
	public class XmlController : IHttpHandler
	{
		private const string EVENT_PARAM = "e";
		private const string STATE_PARAM = "st";
		private const string XML_TRANSITION_COMMAND = "XmlTransitionCommand";

		#region IHttpHandler Members

		/// <summary>
		/// Processes an HTTP XML request.
		/// </summary>
		/// <param name="context">An <c>HttpContext</c> object that provides
		///			references to the intrinsic server objects (for example, 
		///			Request, Response, Session, and Server) used to service 
		///			HTTP requests.</param>
		public void ProcessRequest(
			HttpContext context)
		{
			XmlDocument requestXml = null;
			XmlDocument responseXml = null;
			string response = "";

			//  record the time the request was received
			context.Items["startTime"] = DateTime.Now;

			MvcRequest request = RequestFactory.Make(context);

			try {
				//  if the request has the state (st) and event (e) parameters,
				//  assume we are performing a state transition
				string cmdName;
//			if (request[EVENT_PARAM] != null) {
				if ((request[STATE_PARAM] != null) && (request[EVENT_PARAM] != null)) {
					cmdName = XML_TRANSITION_COMMAND;
				} else {
					//  get the name of the command to carry out the request
					cmdName = this.GetCommandName((XmlDocument) request.Items[MvcXmlRequest.REQUEST_XML]);
				}

				//  get the Command from the factory...
				Command.Command cmd = CommandFactory.Make(cmdName);
				//  and execute it
				cmd.Execute(request);

				//  get the XML response generated by the command
//			responseXml = (XmlDocument)request.Items[MvcXmlRequest.RESPONSE_XML];
//			response = responseXml.OuterXml;
				response = (string) request.Items[MvcXmlRequest.RESPONSE_XML];
			} catch (Exception e) {
				LogManager.GetCurrentClassLogger().Error(
					errorMessage => errorMessage("XmlController.ProcessRequest: "), e);

				responseXml = new XmlDocument();
				//  <response>
				XmlNode responseNode = responseXml.CreateNode(XmlNodeType.Element, "response", null);
				responseXml.AppendChild(responseNode);
				//  <status>
				XmlNode statusNode = responseXml.CreateNode(XmlNodeType.Element, "status", null);
				statusNode.InnerText = "failure";
				responseNode.AppendChild(statusNode);
				//  <data> (empty)
				XmlNode dataNode = responseXml.CreateNode(XmlNodeType.Element, "data", null);
				responseNode.AppendChild(dataNode);
				//  <errors>
				XmlNode errorsNode = responseXml.CreateNode(XmlNodeType.Element, "errors", null);
				responseXml.DocumentElement.AppendChild(errorsNode);
				XmlNode errorNode = responseXml.CreateNode(XmlNodeType.Element, "error", null);
				errorsNode.AppendChild(errorNode);
				errorNode.AppendChild(responseXml.CreateCDataSection(e.Message));
				//  <request>
				responseNode.AppendChild(responseXml.ImportNode(requestXml.DocumentElement.SelectSingleNode("//request"), true));
			}

			//  send the response
			context.Response.Write(response);
		}


		/// <summary>
		/// Returns <c>true</c> if the instance is reusable, <c>false</c> otherwise.
		/// </summary>
		public bool IsReusable
		{
			get { return true; }
		}

		#endregion

		/// <summary>
		/// Gets the name of the <c>Command</c> for carrying out the given request.
		/// </summary>
		/// <param name="xml">The XML request to get the command for.</param>
		/// <returns>The name of the <c>Command</c> for carrying out the given request.</returns>
		private String GetCommandName(
			XmlDocument xml)
		{
			String cmdName = null;

			try {
				XmlNode requestNode = xml.DocumentElement.SelectSingleNode("/request");

				//  if the request has an "action" attribute, get the command to execute
				//  from the Action Map
				//  NOTE: this remains for backward compatibility and should go away someday
				if ((requestNode != null) && (requestNode.Attributes["action"] != null)) {
					//  get the name of the action from the request
					string actionName = requestNode.Attributes["action"].Value;
					//  get the action settings from the action map
					XmlConfiguration action = XmlActionMap.GetInstance().GetConfig("actions", actionName);
					//  get the name of the command from the map
					cmdName = action.GetValue("//command");

					//  if the request has "st" and "e" attributes, use the transition command
				} else if ((requestNode != null) && (requestNode.Attributes["st"] != null) && (requestNode.Attributes["e"] != null)) {
					cmdName = XML_TRANSITION_COMMAND;
				} else {
					throw new Exception("No 'action' or 'st' & 'e' specified in request.");
				}
			} catch (Exception e) {
				LogManager.GetCurrentClassLogger().Error(
					errorMessage => errorMessage("XmlController.GetCommandName: "), e);
				throw;
			}

			return cmdName;
		}
	}
}